#include "bluetooth_app.h"
#include "bluetooth_board.h"
#include "bluetooth_peripheral_service.h"

/**
 * @brief 加载相关参数
 *
 */
static void PeripheralLoadRelatedParameters(void);

/**
 * @brief 重置蓝牙连接时间
 *
 */
static void ResetTheBluetoothConnectionTime(void);

/**
 * @brief 重置蓝牙报文接收时间
 *
 */
static void ExampleResetTheTimeForReceivingBluetoothPackets(void);

/**
 * @brief 发起通知
 *
 * @param pDataIn - 数据报文
 * @param SizeIn  - 数据报文有效长度
 */
static void PeripheralNotify(const uint8_t *pDataIn, uint16_t SizeIn);

/**
 * @brief 初始化连接项目
 *
 */
static void PeripheralInitConnItem(void);

/**
 * @brief 进程链接已建立
 *
 * @param pEvent - 事件句柄
 */
static void PeripheralLinkEstablished(gapRoleEvent_t *pEvent);

/**
 * @brief 进程链接终止
 *
 * @param pEvent - 事件句柄
 */
static void PeripheralLinkTerminated(gapRoleEvent_t *pEvent);

/**
 * @brief 来自配置文件的状态更改通知
 *
 * @param NewState      - 新状态
 * @param pGapRoleEvent - 事件回调
 */
static void PeripheralStateNotificationCallback(gapRole_States_t NewState, gapRoleEvent_t *pGapRoleEvent);

/**
 * @brief 获取当前通信质量
 *
 * @param ConnHandle - 连接句柄
 * @param RSSI       - RSSI
 */
static void PeripheralRssiCallback(uint16_t ConnHandle, int8_t RSSI);

/**
 * @brief 处理传入的任务消息
 *
 * @param pEvent - 任务消息
 */
static void PeripheralProcessGAPMsg(gapRoleEvent_t *pEvent);

/**
 * @brief 参数更新完成处理任务
 *
 * @param ConnHandle       - 连接句柄
 * @param ConnInterval     - 连接间隔
 * @param ConnSlaveLatency - 连接从设备延时
 * @param ConnTimeout      - 连接超时
 */
static void PeripheralParamUpdateCallback(uint16_t ConnHandle, uint16_t ConnInterval, uint16_t ConnSlaveLatency, uint16_t ConnTimeout);

/**
 * @brief 主机写入数据处理任务
 *
 * @param pDataIn - 主机写入的数据
 * @param SizeIn  - 写入数据有效长度
 */
static void PeripheralHostWriteDataHandler(const uint8_t *pDataIn, uint16_t SizeIn);

/**
 * @brief 任务消息处理
 *
 * @param pMsg - 消息
 */
static void PeripheralProcessTMOSMsg(tmos_event_hdr_t *pMsg);

/**
 * @brief 蓝牙事件处理函数
 *
 * @param TaskId - 任务句柄
 * @param Events - 事件类型
 */
static uint16_t PeripheralProcessEvent(uint8_t TaskId, uint16_t Events);

uint8_t NumberOfAttDevice = 0;
uint8_t NumberOfAdvertData = 0;
uint8_t NumberOfScanRspData = 0;

uint8_t PeripheralTaskId = INVALID_TASK_ID;
uint8_t PeripheralMTU = PERIPHERAL_CHARACTERISTIC_LENGTH;

uint8_t AdvertData[B_MAX_ADV_LEN] = {0};
uint8_t ScanRspData[B_MAX_ADV_LEN] = {0};
uint8_t AttDeviceName[GAP_DEVICE_NAME_LEN] = {0};

BluetoothDataBuff_t BluetoothDataBuff = {0};
PeripheralConnItem_t PeripheralConnList = {0};

// GAP Role Callbacks
static gapRolesCBs_t PeripheralPeripheralCallback = {
    PeripheralStateNotificationCallback,
    PeripheralRssiCallback,
    PeripheralParamUpdateCallback,
};

// Broadcast Callbacks
static gapRolesBroadcasterCBs_t BroadcasterBroadcasterCallback = {
    NULL,
    NULL,
};

// GAP Bond Manager Callbacks
static gapBondCBs_t PeripheralBondMgrCallback = {
    NULL,
    NULL,
};

static void PeripheralLoadRelatedParameters(void)
{
  char HostName[GAP_DEVICE_NAME_LEN] = {0};
  uint8_t DeviceWorkMode = 0, HostNameLenght = 0, ProtocolVersion = 0, DeviceType = 0, MacAddr[MAC_ADDR_CODING_LENGTH] = {0};

  BluetoothGetRadioName(HostName);
  BluetoothGetMacAddress(MacAddr);

  HostNameLenght = strlen(HostName);
  DeviceType = BluetoothGetDeviceType();
  DeviceWorkMode = BluetoothGetSystemStatus();
  ProtocolVersion = BluetoothGetProtocolVersion();

  AdvertData[NumberOfAdvertData++] = 2;
  AdvertData[NumberOfAdvertData++] = GAP_ADTYPE_FLAGS;
  AdvertData[NumberOfAdvertData++] = DEFAULT_DISCOVERABLE_MODE | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED;

  AdvertData[NumberOfAdvertData++] = 3;
  AdvertData[NumberOfAdvertData++] = GAP_ADTYPE_16BIT_MORE;
  AdvertData[NumberOfAdvertData++] = LO_UINT16(PERIPHERAL_SERVICE_UUID);
  AdvertData[NumberOfAdvertData++] = HI_UINT16(PERIPHERAL_SERVICE_UUID);

  ScanRspData[NumberOfScanRspData++] = HostNameLenght + 1;
  ScanRspData[NumberOfScanRspData++] = GAP_ADTYPE_LOCAL_NAME_COMPLETE;
  memcpy(&ScanRspData[NumberOfScanRspData], HostName, HostNameLenght);
  NumberOfScanRspData += HostNameLenght;

  ScanRspData[NumberOfScanRspData++] = 5;
  ScanRspData[NumberOfScanRspData++] = GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE;
  ScanRspData[NumberOfScanRspData++] = LO_UINT16(DEFAULT_DESIRED_MIN_CONN_INTERVAL);
  ScanRspData[NumberOfScanRspData++] = HI_UINT16(DEFAULT_DESIRED_MIN_CONN_INTERVAL);
  ScanRspData[NumberOfScanRspData++] = LO_UINT16(DEFAULT_DESIRED_MAX_CONN_INTERVAL);
  ScanRspData[NumberOfScanRspData++] = HI_UINT16(DEFAULT_DESIRED_MAX_CONN_INTERVAL);

  ScanRspData[NumberOfScanRspData++] = 10;
  ScanRspData[NumberOfScanRspData++] = GAP_ADTYPE_MANUFACTURER_SPECIFIC;
  memcpy(&ScanRspData[NumberOfScanRspData], MacAddr, MAC_ADDR_CODING_LENGTH);
  NumberOfScanRspData += MAC_ADDR_CODING_LENGTH;
  ScanRspData[NumberOfScanRspData++] = DeviceWorkMode;
  ScanRspData[NumberOfScanRspData++] = ProtocolVersion;
  ScanRspData[NumberOfScanRspData++] = DeviceType;

  NumberOfAttDevice = HostNameLenght;
  memcpy(AttDeviceName, HostName, HostNameLenght);

  PRINT("[bluetooth]: broadcast length: %d, data:", NumberOfScanRspData);
  for (uint8_t i = 0; i < NumberOfScanRspData; i++)
    PRINT("%02X", ScanRspData[i]);
  PRINT("\r\n");
}

static void ResetTheBluetoothConnectionTime(void)
{
  uint16_t ConnectTimeout = BluetoothGetConnectTimeout();

  tmos_stop_task(PeripheralTaskId, BLUETOOTH_CONNECT_TIMEOUT_EVENT);
  tmos_start_task(PeripheralTaskId, BLUETOOTH_CONNECT_TIMEOUT_EVENT, ConnectTimeout);
}

static void ExampleResetTheTimeForReceivingBluetoothPackets(void)
{
  tmos_stop_task(PeripheralTaskId, BLUETOOTH_RECIVE_TIMEOUT_EVENT);
  tmos_start_task(PeripheralTaskId, BLUETOOTH_RECIVE_TIMEOUT_EVENT, DEFAULT_BLUETOOTH_RECIVE_TIMEOUT);
}

static void PeripheralNotify(const uint8_t *pDataIn, uint16_t SizeIn)
{
  attHandleValueNoti_t NotifyHandler = {0};

  if (SizeIn <= (MAX_BLUETOOTH_MTU_SIZE - 3))
  {
    NotifyHandler.len = SizeIn;
    NotifyHandler.pValue = GATT_bm_alloc(PeripheralConnList.ConnHandle, ATT_HANDLE_VALUE_NOTI, SizeIn, NULL, 0);
    if (NotifyHandler.pValue != NULL)
    {
      memcpy(NotifyHandler.pValue, pDataIn, SizeIn);
      if (ProfileNotify(PeripheralConnList.ConnHandle, &NotifyHandler) != SUCCESS)
      {
        GATT_bm_free((gattMsg_t *)&NotifyHandler, ATT_HANDLE_VALUE_NOTI);
      }
    }
  }
}

static void PeripheralInitConnItem(void)
{
  PeripheralConnList.ConnTimeout = 0;
  PeripheralConnList.ConnInterval = 0;
  PeripheralConnList.ConnSlaveLatency = 0;
  PeripheralConnList.ConnHandle = GAP_CONNHANDLE_INIT;
}

static void PeripheralLinkEstablished(gapRoleEvent_t *pEvent)
{
  gapEstLinkReqEvent_t *LinkEvent = (gapEstLinkReqEvent_t *)pEvent;

  if (PeripheralConnList.ConnHandle != GAP_CONNHANDLE_INIT)
  {
    PRINT("[bluetooth]: bluetooth connection max.\n");
    GAPRole_TerminateLink(pEvent->linkCmpl.connectionHandle);
  }
  else
  {
    PRINT("[bluetooth]: bluetooth established connection id: %02X, interval: %02X.\r\n", LinkEvent->connectionHandle, LinkEvent->connInterval);

    PeripheralMTU = PERIPHERAL_CHARACTERISTIC_LENGTH;
    PeripheralConnList.ConnTimeout = LinkEvent->connTimeout;
    PeripheralConnList.ConnInterval = LinkEvent->connInterval;
    PeripheralConnList.ConnHandle = LinkEvent->connectionHandle;
    PeripheralConnList.ConnSlaveLatency = LinkEvent->connLatency;

    ResetTheBluetoothConnectionTime();

    tmos_start_task(PeripheralTaskId, BLUETOOTH_PARAM_UPDATE_EVENT, SBP_PARAM_UPDATE_DELAY);
    tmos_start_task(PeripheralTaskId, BLUETOOTH_READ_RSSI_EVENT, SBP_READ_RSSI_EVT_PERIOD);
  }
}

static void PeripheralLinkTerminated(gapRoleEvent_t *pEvent)
{
  gapTerminateLinkEvent_t *LinkEvent = (gapTerminateLinkEvent_t *)pEvent;

  if (LinkEvent->connectionHandle == PeripheralConnList.ConnHandle)
  {
    PeripheralConnList.ConnTimeout = 0;
    PeripheralConnList.ConnInterval = 0;
    PeripheralConnList.ConnSlaveLatency = 0;
    PeripheralConnList.ConnHandle = GAP_CONNHANDLE_INIT;

    uint8_t AdvertisingEnable = true;
    GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, 1, &AdvertisingEnable);
  }
}

static void PeripheralStateNotificationCallback(gapRole_States_t NewState, gapRoleEvent_t *pGapRoleEvent)
{
  switch (NewState & GAPROLE_STATE_ADV_MASK)
  {
  case GAPROLE_STARTED:
  {
    PRINT("[bluetooth]: initialized.\r\n");
  }
  break;

  case GAPROLE_ADVERTISING:
  {
    PRINT("[bluetooth]: advertising.\r\n");

    if (pGapRoleEvent->gap.opcode == GAP_LINK_TERMINATED_EVENT)
    {
      PeripheralLinkTerminated(pGapRoleEvent);
    }
  }
  break;

  case GAPROLE_CONNECTED:
  {
    if (pGapRoleEvent->gap.opcode == GAP_LINK_ESTABLISHED_EVENT)
    {
      PRINT("[bluetooth]: connected.\r\n");

      PeripheralLinkEstablished(pGapRoleEvent);
    }
  }
  break;

  case GAPROLE_CONNECTED_ADV:
  {
    PRINT("[bluetooth]: connected advertising.\r\n");
  }
  break;

  case GAPROLE_WAITING:
  {
    if (pGapRoleEvent->gap.opcode == GAP_END_DISCOVERABLE_DONE_EVENT)
    {
      PRINT("[bluetooth]: waiting for advertising.\r\n");
    }
    else if (pGapRoleEvent->gap.opcode == GAP_LINK_TERMINATED_EVENT)
    {
      PRINT("[bluetooth]: disconnected, reason: %x.\r\n", pGapRoleEvent->linkTerminate.reason);

      PeripheralLinkTerminated(pGapRoleEvent);
    }
    else if (pGapRoleEvent->gap.opcode == GAP_LINK_ESTABLISHED_EVENT)
    {
      if (pGapRoleEvent->gap.hdr.status != SUCCESS)
      {
        PRINT("[bluetooth]: waiting for advertising.\r\n");
      }
    }
  }
  break;

  default:
    break;
  }
}

static void PeripheralRssiCallback(uint16_t ConnHandle, int8_t Rssi)
{
  PRINT("[bluetooth]: peripheral rssi: -%d dB connection id: %02X.\r\n", -Rssi, ConnHandle);
}

static void PeripheralProcessGAPMsg(gapRoleEvent_t *pEvent)
{
  switch (pEvent->gap.opcode)
  {
  case GAP_SCAN_REQUEST_EVENT:
  {
    PRINT("[bluetooth]: received a scanf from: %X-%X-%X-%X-%X%X.\r\n", pEvent->scanReqEvt.scannerAddr[0],
          pEvent->scanReqEvt.scannerAddr[1], pEvent->scanReqEvt.scannerAddr[2], pEvent->scanReqEvt.scannerAddr[3],
          pEvent->scanReqEvt.scannerAddr[4], pEvent->scanReqEvt.scannerAddr[5]);
  }
  break;

  case GAP_PHY_UPDATE_EVENT:
  {
    PRINT("[bluetooth]: phy update rx: %X tx: %X\r\n", pEvent->linkPhyUpdate.connRxPHYS, pEvent->linkPhyUpdate.connTxPHYS);
  }
  break;

  default:
    break;
  }
}

static void PeripheralParamUpdateCallback(uint16_t ConnHandle, uint16_t ConnInterval, uint16_t ConnSlaveLatency, uint16_t ConnTimeout)
{
  if (ConnHandle == PeripheralConnList.ConnHandle)
  {
    PeripheralConnList.ConnTimeout = ConnTimeout;
    PeripheralConnList.ConnInterval = ConnInterval;
    PeripheralConnList.ConnSlaveLatency = ConnSlaveLatency;

    PRINT("[bluetooth]: update connection id: %02X, interval: %02X.\r\n", ConnHandle, ConnInterval);
  }
}

static void PeripheralHostWriteDataHandler(const uint8_t *pDataIn, uint16_t SizeIn)
{
  if ((BluetoothDataBuff.DataSize + SizeIn) < BLUETOOTH_MESSAGE_PACKET_LENGTH)
  {
    memcpy(&BluetoothDataBuff.DataBuff[BluetoothDataBuff.DataSize], pDataIn, SizeIn);
    BluetoothDataBuff.DataSize += SizeIn;
  }

  ResetTheBluetoothConnectionTime();
  ExampleResetTheTimeForReceivingBluetoothPackets();
}

static void PeripheralProcessTMOSMsg(tmos_event_hdr_t *pMsg)
{
  switch (pMsg->event)
  {
  case GAP_MSG_EVENT:
  {
    PeripheralProcessGAPMsg((gapRoleEvent_t *)pMsg);
  }
  break;

  case GATT_MSG_EVENT:
  {
    gattMsgEvent_t *pMsgEvent = (gattMsgEvent_t *)pMsg;

    if (pMsgEvent->method == ATT_MTU_UPDATED_EVENT)
    {
      PeripheralMTU = pMsgEvent->msg.exchangeMTUReq.clientRxMTU;
      PRINT("[bluetooth]: mtu exchange: %d\r\n", PeripheralMTU);
    }
  }
  break;

  default:
    break;
  }
}

static uint16_t PeripheralProcessEvent(uint8_t TaskId, uint16_t Events)
{
  if (TaskId == PeripheralTaskId)
  {
    if (Events & SYS_EVENT_MSG)
    {
      uint8_t *pMessageHandler = tmos_msg_receive(PeripheralTaskId);

      if (pMessageHandler != NULL)
      {
        PeripheralProcessTMOSMsg((tmos_event_hdr_t *)pMessageHandler);
        tmos_msg_deallocate(pMessageHandler);
      }

      return (Events ^ SYS_EVENT_MSG);
    }

    if (Events & BLUETOOTH_CONNECT_TIMEOUT_EVENT)
    {
      GAPRole_TerminateLink(PeripheralConnList.ConnHandle);
      return (Events ^ BLUETOOTH_CONNECT_TIMEOUT_EVENT);
    }

    if (Events & BLUETOOTH_RECIVE_TIMEOUT_EVENT)
    {
      BluetoothAnalysisServerPushMessage(BluetoothDataBuff.DataBuff);
      memset(&BluetoothDataBuff, 0, sizeof(BluetoothDataBuff_t));

      return (Events ^ BLUETOOTH_RECIVE_TIMEOUT_EVENT);
    }

    if (Events & BLUETOOTH_START_DEVICE_EVENT)
    {
      GAPRole_PeripheralStartDevice(PeripheralTaskId, &PeripheralBondMgrCallback, &PeripheralPeripheralCallback);
      return (Events ^ BLUETOOTH_START_DEVICE_EVENT);
    }

    if (Events & BLUETOOTH_PARAM_UPDATE_EVENT)
    {
      GAPRole_PeripheralConnParamUpdateReq(PeripheralConnList.ConnHandle,
                                           DEFAULT_DESIRED_MIN_CONN_INTERVAL,
                                           DEFAULT_DESIRED_MAX_CONN_INTERVAL,
                                           DEFAULT_DESIRED_SLAVE_LATENCY,
                                           DEFAULT_DESIRED_CONN_TIMEOUT,
                                           PeripheralTaskId);

      return (Events ^ BLUETOOTH_PARAM_UPDATE_EVENT);
    }

    if (Events & BLUETOOTH_READ_RSSI_EVENT)
    {
      GAPRole_ReadRssiCmd(PeripheralConnList.ConnHandle);
      return (Events ^ BLUETOOTH_READ_RSSI_EVENT);
    }

    if (Events & BLUETOOTH_PHY_UPDATE_EVENT)
    {
      PRINT("PHY Update %x...\n", GAPRole_UpdatePHY(PeripheralConnList.ConnHandle, 0, GAP_PHY_BIT_LE_2M, GAP_PHY_BIT_LE_2M, 0));
      return (Events ^ BLUETOOTH_PHY_UPDATE_EVENT);
    }

    if (Events & BLUETOOTH_SEND_DATA_TO_HOST_EVENT)
    {
      uint16_t NumberOfTransmit = 0;
      uint8_t *pSendDataBuff = &BluetoothDataBuff.DataBuff[BluetoothDataBuff.LengthOfTransmit];

      {
        NumberOfTransmit = MAX_BLUETOOTH_MTU_SIZE - 3;
        if ((BluetoothDataBuff.DataSize - BluetoothDataBuff.LengthOfTransmit) < NumberOfTransmit)
        {
          NumberOfTransmit = BluetoothDataBuff.DataSize - BluetoothDataBuff.LengthOfTransmit;
        }
      }

      {
        PeripheralNotify(pSendDataBuff, NumberOfTransmit);

        BluetoothDataBuff.LengthOfTransmit += NumberOfTransmit;
        if (BluetoothDataBuff.LengthOfTransmit < BluetoothDataBuff.DataSize)
        {
          tmos_start_task(PeripheralTaskId, BLUETOOTH_SEND_DATA_TO_HOST_EVENT, DEFAULT_BLUETOOTH_TRANSMIT_DELAY);
        }
        else
        {
          memset(&BluetoothDataBuff, 0, sizeof(BluetoothDataBuff_t));
        }
      }

      return (Events ^ BLUETOOTH_SEND_DATA_TO_HOST_EVENT);
    }
  }

  return 0;
}

void BluetoothGetMacAddr(uint8_t *pMacAddrOut)
{
  BluetoothGetMacAddress(pMacAddrOut);
}

void BluetoothUpdatingTheBroadcastPackage(void)
{
  PeripheralLoadRelatedParameters();

  GAP_UpdateAdvertisingData(PeripheralTaskId, true, NumberOfAdvertData, AdvertData);
  GAP_UpdateAdvertisingData(PeripheralTaskId, false, NumberOfScanRspData, ScanRspData);
}

bool BluetoothSendDataToHost(uint8_t *pDataIn, uint16_t DataSize)
{
  if (pDataIn != NULL)
  {
    if ((DataSize != 0) && (DataSize <= BLUETOOTH_MESSAGE_PACKET_LENGTH))
    {
      tmos_set_event(PeripheralTaskId, BLUETOOTH_SEND_DATA_TO_HOST_EVENT);

      BluetoothDataBuff.DataSize = DataSize;
      memcpy(BluetoothDataBuff.DataBuff, pDataIn, DataSize);

      return true;
    }
  }

  return false;
}

void BluetoothPeripheralInit(void)
{
  PeripheralTaskId = TMOS_ProcessEventRegister(PeripheralProcessEvent);

  {
    uint16_t DesiredMinInterval = DEFAULT_DESIRED_MIN_CONN_INTERVAL;
    uint16_t DesiredMaxInterval = DEFAULT_DESIRED_MAX_CONN_INTERVAL;

    PeripheralLoadRelatedParameters();
    GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, 1, (uint8_t[]){true});
    GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, NumberOfScanRspData, ScanRspData);
    GAPRole_SetParameter(GAPROLE_ADVERT_DATA, NumberOfAdvertData, AdvertData);
    GAPRole_SetParameter(GAPROLE_MIN_CONN_INTERVAL, sizeof(uint16_t), &DesiredMinInterval);
    GAPRole_SetParameter(GAPROLE_MAX_CONN_INTERVAL, sizeof(uint16_t), &DesiredMaxInterval);
  }

  {
    uint16_t AdvInterval = BluetoothGetBroadcastInterval();

    GAP_SetParamValue(TGAP_DISC_ADV_INT_MIN, AdvInterval);
    GAP_SetParamValue(TGAP_DISC_ADV_INT_MAX, AdvInterval);
  }

  {
    uint32_t passkey = 0;
    uint8_t mitm = true;
    uint8_t bonding = true;
    uint8_t ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY;
    uint8_t pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ;

    GAPBondMgr_SetParameter(GAPBOND_PERI_DEFAULT_PASSCODE, sizeof(uint32_t), &passkey);
    GAPBondMgr_SetParameter(GAPBOND_PERI_PAIRING_MODE, sizeof(uint8_t), &pairMode);
    GAPBondMgr_SetParameter(GAPBOND_PERI_MITM_PROTECTION, sizeof(uint8_t), &mitm);
    GAPBondMgr_SetParameter(GAPBOND_PERI_IO_CAPABILITIES, sizeof(uint8_t), &ioCap);
    GAPBondMgr_SetParameter(GAPBOND_PERI_BONDING_ENABLED, sizeof(uint8_t), &bonding);
  }

  GGS_AddService(GATT_ALL_SERVICES);
  GATTServApp_AddService(GATT_ALL_SERVICES);
  ProfileAddService(PeripheralHostWriteDataHandler);
  GGS_SetParameter(GGS_DEVICE_NAME_ATT, NumberOfAttDevice, AttDeviceName);

  PeripheralInitConnItem();
  GAPRole_BroadcasterSetCB(&BroadcasterBroadcasterCallback);

  tmos_set_event(PeripheralTaskId, BLUETOOTH_START_DEVICE_EVENT);
}
